#include <stdlib.h>
#include <apr_pools.h>
#include "mpi.h"
#include "oom.h"
#include "env.h"
#include "cm.h"

/** \file groups_comms.c
 * \brief File contains all of the structure for MPI group communications
 */

int negotiate_context_tag(Comm *comm, Group *group,
	uid_pool *context_tag_pool);
		
/**
 * \brief returns the size of a group
 * \param group group(handle) output parameter
 * \param size number of processes in the group (integer)
 * \return status value
 */
int MPI_Group_size(MPI_Group group, int *size)
{
	Group *real_group;
	
	if (group == MPI_GROUP_NULL)
	{
		return MPI_ERR_ARG;
	}
	else if (group == MPI_GROUP_EMPTY)
	{
		*size = 0;
		return MPI_SUCCESS;
	}
	else
	{
		real_group = OOM_group_lookup(group);
		if (real_group == NULL)
		{
			return MPI_ERR_ARG;
		}
		else
		{
			*size = real_group->num_members;
			return MPI_SUCCESS;
		}
	}
}

/**
 * \brief returns the rank of this process in the given group
 * \param group group handle
 * \return status parameter
 */
int MPI_Group_rank(MPI_Group group, int *rank)
{
	Group *real_group;
	
	if (group == MPI_GROUP_NULL)
	{
		return MPI_ERR_ARG;
	}
	else if (group == MPI_GROUP_EMPTY)
	{
		*rank = MPI_UNDEFINED;
		return MPI_SUCCESS;
	}
	else
	{
		real_group = OOM_group_lookup(group);
		if (real_group == NULL)
		{
			return MPI_ERR_ARG;
		}
		else
		{
			*rank = MPI_UNDEFINED;
			int i;
			for (i = 0; i < real_group->num_members; i++)
			{
				if (real_group->members[i] == get_world_rank())
				{
					*rank = i;
					break;
				}
			}
			return MPI_SUCCESS;
		}
	}
}

/**
 * \author James Edmondson
 * \brief Translates the ranks of processes in one group to those in
 *        another group
 * \param group1 group1(handle)
 * \param n number of ranks in ranks1 and ranks2 arrays (integer)
 * \param ranks1 array of zero or more valid ranks in group1
 * \param group2 group2 handle
 * \param ranks2 array of corresponding ranks in group2,
 *               MPI_UNDEFINED when no correspondence exists
 * \return status code
 */

int MPI_Group_translate_ranks(MPI_Group group1, int n, int *ranks1,
	MPI_Group group2, int *ranks2)
{
	int i = 0, j = 0;
	int world_rank = 0;
	int found = 0;
	int cur = 0;
	Group * real_group1 = OOM_group_lookup(group1);
	Group * real_group2 = OOM_group_lookup(group2);

	if( real_group1 == NULL || real_group2 == NULL )
	{
		return MPI_ERR_GROUP;
	}
	
	for( i = 0; i < n; i++ )
	{
		found = 0;
		cur = ranks1[i];
		
		if( cur < 0 || cur > real_group1->num_members )
		{
			if( cur >= 0 ) cur = MPI_UNDEFINED;
			ranks2[i] = cur;
			continue;
		}
		
		world_rank = real_group1->members[i];
		
		for( j = 0; j < real_group2->num_members && !found; j++ )
		{
			if( world_rank == real_group2->members[j] )
			{
				found = 1;
				ranks2[i] = j;
			}
		}
		
		if( !found )
		{
			ranks2[i] = MPI_UNDEFINED;
		}
	}
	
	return MPI_SUCCESS;
}

/**
 * \brief compare two groups
 * \param group1 group1 (handle)
 * \param group2 group2 (handle)
 * \param result integer which is MPI_IDENT if the order and members of the two groups are the same, MPI_SIMILAR if only the members are the same, and MPI_UNEQUAL otherwise (output)
 * \return status parameters
 */
int MPI_Group_compare(MPI_Group group1, MPI_Group group2, int *result)
{
	Group *group1Info;
	Group *group2Info;
	MPI_Group result_group;
	int ix, size;

	if ((group1 == MPI_GROUP_NULL) || (group2 == MPI_GROUP_NULL))
	{
		return MPI_ERR_ARG;
	}

	/* Get group 1 */
	group1Info = OOM_group_lookup(group1);
	if(group1Info == NULL)
	{
		return MPI_ERR_ARG;
	}

	/* Get group 2 */
	group2Info = OOM_group_lookup(group2);
	if(group2Info == NULL)
	{
		return MPI_ERR_ARG;
	}

	/* Check to see if the number of members are the same */
	if(group1Info->num_members != group2Info->num_members)
	{
		/* Number of members are not the same, return MPI_UNEQUAL */
		*result = MPI_UNEQUAL;
		return MPI_SUCCESS;
	}

	/* Perform an intersection of the groups -- see if they are the same size */
	MPI_Group_intersection(group1, group2, &result_group);
	MPI_Group_size(result_group, &size);
	MPI_Group_free(&result_group);

	if(size != group1Info->num_members)
	{
		*result = MPI_UNEQUAL;
		return MPI_SUCCESS;
	}

	/* Groups have same members ... see if they are in the same order */
	*result = MPI_SIMILAR;

	/* Check to see if the members are in the same order */
	for(ix = 0; ix < group1Info->num_members; ix++)
	{
		if(group1Info->members[ix] != group2Info->members[ix])
		{
			/* Members not in the same order */
			return MPI_SUCCESS;
		}
	}

	/* If we are here, the groups are identical */
	*result = MPI_IDENT;
	return MPI_SUCCESS;
}

/**
 * \brief Accesses the group associated with given communicator
 * \param comm Communicator
 * \group group in communicator (output)
 * \return status code
 */
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group)
{
	Comm *real_comm;
	
	if (comm == MPI_COMM_NULL)
	{
		return MPI_ERR_COMM;
	}
	else 
	{
		real_comm = OOM_comm_lookup(comm);
		if (real_comm == NULL)
		{
			return MPI_ERR_COMM;
		}
		else
		{
			*group = real_comm->group;
			return MPI_SUCCESS;
		}
	}
}

/**
 * \brief Produces a group by combining two groups
 * \param group1 first group (handle)
 * \param group2 second group (handle)
 * \return status code
 */
int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup)
{
	/* local variables */
	Group *group1Info;
	Group *group2Info;
	int *unionMembers;
	int ix, iy, count, isFound;
	apr_pool_t *mempool;

	/* Get group 1 */
	group1Info = OOM_group_lookup(group1);
	if(group1Info == NULL)
	{
        return MPI_ERR_ARG;
	}

	/* Get group 2 */
	group2Info = OOM_group_lookup(group2);
	if(group2Info == NULL)
	{
        return MPI_ERR_ARG;
	}
	
	/* create a memory pool for allocating temporary storage */
	apr_pool_create(&mempool, NULL);
	
	/* Get some memory to store the members of the group union */
	unionMembers = (int *) apr_palloc(mempool,
		(group1Info->num_members + group2Info->num_members) * sizeof(int));

	/* Get the union of the two groups */
	count = 0;

	/* Load up the unionMembers array with the members from group1 */
	for(ix = 0; ix < group1Info->num_members; ix++)
	{
		unionMembers[count++] = group1Info->members[ix];
	}
	
	/* Look at members in group 2 and determine if they are already in the union.
	   If not, add to the union */
	for(ix = 0; ix < group2Info->num_members; ix++)
	{
		isFound = 0;
		for(iy = 0; iy < group1Info->num_members; iy++)
		{
			if(group2Info->members[ix] == group1Info->members[iy])
			{
				isFound = 1;
				break;
			}
		}
		if(!isFound)
		{
			unionMembers[count++] = group2Info->members[ix];
		}
	}
 
	/* Create the new group */
	*newgroup = OOM_group_create(unionMembers, count);

	/* free up temporary storage */
	apr_pool_destroy(mempool);

	return MPI_SUCCESS;
}

/**
 * \brief Produces a group as the intersection of two existing groups
 * \param group1 first group (handle)
 * \param group2 second group (handle)
 * \return status code
 */
int MPI_Group_intersection(MPI_Group group1, MPI_Group group2,
	MPI_Group *newgroup)
{
	/* local variables */
	Group * group1Info;
	Group * group2Info;
	int *   intersectionMembers;
	int		ix, iy, count;
	apr_pool_t *mempool;

	/* Get group 1 */
	group1Info = OOM_group_lookup(group1);

	if(group1Info == NULL)
	{
        return MPI_ERR_ARG;
	}

	/* Get group 2 */
	group2Info = OOM_group_lookup(group2);
	
	if(group2Info == NULL)
	{
        return MPI_ERR_ARG;
	}
	
	/* create a memory pool for allocating temporary storage */
	apr_pool_create(&mempool, NULL);
	
	/* Get some memory to store the members of the group intersection */
	intersectionMembers = (int *) apr_palloc(mempool,
		((group1Info->num_members < group2Info->num_members)
			? group2Info->num_members : group1Info->num_members) * sizeof(int));

	/* Get intersection */
	count = 0;
	for(ix = 0; ix < group1Info->num_members; ix++)
	{
		for(iy = 0; iy < group2Info->num_members; iy++)
		{
			if(group1Info->members[ix] == group2Info->members[iy])
			{
				intersectionMembers[count++] = group1Info->members[ix];
			}
		}
	}

	/* Create the new group */
	*newgroup = OOM_group_create(intersectionMembers, count);

	/* free up memory */
	apr_pool_destroy(mempool);

	/* return */
	return MPI_SUCCESS;	
}

/**
 * \brief makes a group from the difference of two groups
 * \param group1 first group (handle)
 * \param group2 second group (handle)
 * \return status code
 */
int MPI_Group_difference(MPI_Group group1, MPI_Group group2,
	MPI_Group *newgroup)
{
	Group * group1Info;
	Group * group2Info;
	int *	diffMembers;
	int		ix, iy, found, count;
	apr_pool_t *mempool;

	/* Get group 1 */
	group1Info = OOM_group_lookup(group1);

	if(group1Info == NULL)
	{
        return MPI_ERR_ARG;
	}

	/* Get group 2 */
	group2Info = OOM_group_lookup(group2);
	
	if(group2Info == NULL)
	{
        return MPI_ERR_ARG;
	}
	
	/* create a memory pool for allocating temporary storage */
	apr_pool_create(&mempool, NULL);

	/* Get some memory to store the members of the group difference */
	diffMembers = (int *) apr_palloc(mempool,
		group1Info->num_members * sizeof(int));

	/* Get the difference of the two groups */
	count = 0;

	for(ix = 0; ix < group1Info->num_members; ix++)
	{
		/* Check to see if this element is in the second group */
		found = 0;
		for(iy = 0; iy < group2Info->num_members; iy++)
		{
			if(group1Info->members[ix] == group2Info->members[iy])
			{
				found = 1;
				break;
			}
			if(!found)
			{
				diffMembers[count++] = group1Info->members[ix];
			}
		}
	}

	/* Create the new group */
	*newgroup = OOM_group_create(diffMembers, count);

	/* free up memory */
	apr_pool_destroy(mempool);

	return MPI_SUCCESS;	
}

/**
 * \brief produces a group by reordering an existing group and taking only listed members
 * \param group group (handle)
 * \param n number of elements in array ranks (and size of newgroup)
 * \param ranks ranks of processes in group to appear in newgroup
 * \param newgroup new group derived from above, in the order defined by ranks
 * \return status code
 */
int MPI_Group_incl(MPI_Group group, int n, int * ranks,	MPI_Group *newgroup)
{
	Group * groupInfo;
	apr_pool_t *mempool;
	int *incMembers;
	int	ix;

	/* Get group info */
	
	groupInfo = OOM_group_lookup(group);
	if(groupInfo == NULL)
	{
        return MPI_ERR_ARG;
	}

	/* Get some memory to store the members of the group include */
	apr_pool_create(&mempool, NULL);
	incMembers = (int *) apr_palloc(mempool, n * sizeof(int));

	/* Get the members of the new group */
	for(ix = 0; ix < n; ix++)
	{
		incMembers[ix] = groupInfo->members[ranks[ix]];
	}

	/* Create the new group */
	*newgroup = OOM_group_create(incMembers, n);

	/* free up memory */
	apr_pool_destroy(mempool);

	return MPI_SUCCESS;	
}

/**
 * \brief produces a group by reordering an existing group and taking only unlisted members
 * \param group group (handle)
 * \param n number of elements in array ranks (integer)
 * \param ranks array of integer ranks in group not to appear in newgroup
 * \param newgroup new group derived from above preserving the order defined by group
 * \return status code
 */
int MPI_Group_excl(MPI_Group group, int n, int * ranks,	MPI_Group *newgroup)
{
	Group * groupInfo;
	int *	exclMembers;
	int		ix, iy, found, count;
	apr_pool_t *mempool;

	/* Get group info */
	groupInfo = OOM_group_lookup(group);

	if(groupInfo == NULL)
	{
        return MPI_ERR_ARG;
	}

	/* create a memory pool for allocating temporary storage */
	apr_pool_create(&mempool, NULL);
	
	/* Get some memory to store the members of the group exclude */
	exclMembers = (int *) apr_palloc(mempool, n * sizeof(int));

	/* Determine which members are to be excluded and build group
	   from remaining members */
	count = 0;
	for(ix = 0; ix < groupInfo->num_members; ix++)
	{
		found = 0;
		for(iy = 0; iy < n; iy++)
		{
			if(ix == ranks[iy])
			{
				found = 1;
				break;
			}
		}

		/* Check to see if the member was found in the exclude list 
		   If not, add to the list */
		if(!found)
		{
			exclMembers[count++] = groupInfo->members[ix];
		}
	}

	/* Create the new group */
	*newgroup = OOM_group_create(exclMembers, count);

	/* free up memory */
	apr_pool_destroy(mempool);

	return MPI_SUCCESS;
}

/**
 * \brief frees a group
 * \param group group (handle)
 * \return status code
 */
int MPI_Group_free(MPI_Group *group)
{
	Group *real_group;
	if ((*group == MPI_GROUP_NULL) || (*group == MPI_GROUP_EMPTY))
	{
		return MPI_ERR_ARG;
	}
	else
	{
		real_group = OOM_group_lookup(*group);
		if ((real_group == NULL) || (real_group->is_permanent))
		{
			return MPI_ERR_ARG;
		}
		else
		{
			OOM_group_destroy(*group);
			*group = MPI_GROUP_NULL;
			return MPI_SUCCESS;
		}
	}
}

/**
 * \brief Determines the size of the group associated with a communictor
 * \param comm communicator (handle)
 * \param size number of processes in the group of comm (integer)
 * \return status code
 */
int MPI_Comm_size(MPI_Comm comm, int *size)
{
	Comm *real_comm;
	
	if (comm == MPI_COMM_NULL)
	{
		return MPI_ERR_COMM;
	}
	else
	{
		real_comm = OOM_comm_lookup(comm);
		if (real_comm == NULL)
		{
			return MPI_ERR_COMM;
		}
		else
		{
			return MPI_Group_size(real_comm->group, size);
		}
	}
}

/**
 * \brief Determines the rank of the calling process in the communicator 
 * \param comm communicator (handle)
 * \param rank of the calling process in group of comm (integer) 
 * \return status code
 */
int MPI_Comm_rank(MPI_Comm comm, int *rank)
{
	Comm *real_comm;
	
	if (comm == MPI_COMM_NULL)
	{
		return MPI_ERR_COMM;
	}
	else
	{
		real_comm = OOM_comm_lookup(comm);
		if (real_comm == NULL)
		{
			return MPI_ERR_COMM;
		}
		else
		{
			return MPI_Group_rank(real_comm->group, rank);
		}
	}
}

/**
 * \brief Compares two communicators 
 * \param comm1  communicator1 (handle) 
 * \param comm2  communicator2 (handle) 
 * \param result integer which is MPI_IDENT if the contexts and groups are the same, MPI_CONGRUENT if different contexts but identical groups, MPI_SIMILAR if different contexts but similar groups, and MPI_UNEQUAL otherwise
 * \return status code
 */
int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int *results)
{
	Comm *real_comm1, *real_comm2;
	int group_results;
	
	real_comm1 = OOM_comm_lookup(comm1);
	if (real_comm1 == NULL) return MPI_ERR_COMM;
	
	real_comm2 = OOM_comm_lookup(comm2);
	if (real_comm2 == NULL) return MPI_ERR_COMM;
	
	MPI_Group_compare(real_comm1->group, real_comm2->group, &group_results);
	
	if (real_comm1->p2p_context == real_comm2->p2p_context
		&& real_comm2->coll_context == real_comm2->coll_context
		&& group_results == MPI_IDENT)
	{
		*results = MPI_IDENT;
	}
	else if (group_results == MPI_IDENT)
	{
		*results = MPI_CONGRUENT;
	}
	else
	{
		*results = group_results;
	}

	return MPI_SUCCESS;
}

/**
 * \brief Duplicates an existing communicator with all its cached information 
 * \param comm communicator (handle) 
 * \param newcomm A new communicator over the same group as comm but with a new context. (handle)
 * \return status code
 */
int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm)
{
	Comm *real_comm;
	Group *real_group;
	int new_p2p_context, new_coll_context;
	uid_pool *context_tag_pool;
	
	real_comm = OOM_comm_lookup(comm);
	if (real_comm == NULL) return MPI_ERR_COMM;
	real_group = OOM_group_lookup(real_comm->group);
	if (real_group == NULL) return MPI_ERR_COMM;
	context_tag_pool = OOM_get_context_tag_pool();
	
	new_p2p_context =
		negotiate_context_tag(real_comm, real_group, context_tag_pool);
	uid_reserve(context_tag_pool, new_p2p_context);
	uid_reserve_next(context_tag_pool, &new_coll_context);
	
	*newcomm = OOM_comm_create(real_comm->group, new_p2p_context,
		new_coll_context, real_comm->source);
	
	return MPI_SUCCESS;
}

/**
 * \brief Creates a new communicator
 * \param comm communicator (handle) 
 * \param group group, which is a subset of the group of comm (handle) 
 * \return status code
 */
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm)
{
	Comm *real_comm;
	Group *real_group;
	int my_group_rank, new_p2p_context, new_coll_context;
	uid_pool *context_tag_pool;
	
	real_comm = OOM_comm_lookup(comm);
	if (real_comm == NULL) return MPI_ERR_COMM;
	real_group = OOM_group_lookup(real_comm->group);
	if (real_group == NULL) return MPI_ERR_COMM;
	context_tag_pool = OOM_get_context_tag_pool();
	
	/* FIXME: check that group is a subset of comm- */
	
	MPI_Group_rank(group, &my_group_rank);

	new_p2p_context =
		negotiate_context_tag(real_comm, real_group, context_tag_pool);
	uid_reserve(context_tag_pool, new_p2p_context);
	uid_reserve_next(context_tag_pool, &new_coll_context);
	
	if (my_group_rank == MPI_UNDEFINED)
	{
		*newcomm = MPI_COMM_NULL;
	}
	else
	{
		*newcomm = OOM_comm_create(group, new_p2p_context,
			new_coll_context, my_group_rank);
	}
	
	return MPI_SUCCESS;
}

/**
 * \brief Creates new communicators based on colors and keys 
 * \param comm communicator (handle) 
 * \param color control of subset assignment (nonnegative integer). Processes with the same color are in the same new communicator  
 * \param key  control of rank assigment (integer) 
 * \return status code
 */
int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Marks the communicator object for deallocation 
 * \param comm communicator to be destroyed (handle)
 * \return status code
 */
int MPI_Comm_free(MPI_Comm *comm)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Tests to see if a comm is an inter-communicator 
 * \param comm communicator (handle)
 * \param flag logical
 * \return status code
 */
int MPI_Comm_test_inter(MPI_Comm comm, int *flag)
{
	/* intercommunicators not implemented; only checking for valid
	   communicator */
	Comm *real_comm;
	if (comm == MPI_COMM_NULL)
	{
		return MPI_ERR_COMM;
	}
	else
	{
		real_comm = OOM_comm_lookup(comm);
		if (real_comm == NULL)
		{
			return MPI_ERR_COMM;
		}
		else
		{
			*flag = 0;
			return MPI_SUCCESS;
		}
	}
}

/**
 * \brief Determines the size of the remote group associated with an inter-communictor 
 * \param comm communicator (handle)
 * \param size number of processes in the group of comm (integer)
 * \return status code
 */
int MPI_Comm_remote_size(MPI_Comm comm, int *size)
{
	/* intercommunicators not implemented */
	return MPI_ERR_COMM;
}

/**
 * \brief Accesses the remote group associated with the given inter-communicator 
 * \param comm communicator (handle)
 * \param group remote group of communicator 
 * \return status code
 */
int MPI_Comm_remote_group(MPI_Comm comm, MPI_Group *group)
{
	/* intercommunicators not implemented */
	return MPI_ERR_COMM;
}

/**
 * \brief Creates an intercommuncator from two intracommunicators 
 * \param local_comm Local (intra)communicator
 * \param local_leader Rank in local_comm of leader (often 0)  
 * \param peer_comm Remote communicator
 * \param remote_leader Rank in peer_comm of remote leader (often 0) 
 * \param tag Message tag to use in constructing intercommunicator; if multiple MPI_Intercomm_creates are being made, they should use different tags (more precisely, ensure that the local and remote leaders are using different tags for each MPI_intercomm_create).  
 * \param comm_out Created intercommunicator 
 * \return status code
 */
int MPI_Intercomm_create(MPI_Comm local_comm, int local_leader,
	MPI_Comm peer_comm, int remote_leader, int tag,
	MPI_Comm *comm_out)
{
	/* intercommunicators not implemented */
	return MPI_ERR_INTERN;
}

/**
 * \brief Creates an intracommuncator from an intercommunicator 
 * \param comm Intercommunicator 
 * \param high Used to order the groups of the two intracommunicators within comm when creating the new communicator. 
 * \param comm_out Created intracommunicator 
 * \return status code
 */
int MPI_Intercomm_merge(MPI_Comm intercomm, int high,
	MPI_Comm *comm_out)
{
	/* intercommunicators not implemented */
	return MPI_ERR_INTERN;
}

/**
 * \brief Generates a new attribute key
 * \param copy_fn Copy callback function for keyval
 * \param delete_fn Delete callback function for keyval
 * \param keyval key value for future access (integer) 
 * \param extra_state Extra state for callback functions 
 * \return status code
 */
int MPI_Keyval_create(MPI_Copy_function *copy_fn,
	MPI_Delete_function *delete_fn, int *keyval, void *extra_state)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Frees attribute key for communicator cache attribute 
 * \param keyval key value for future access (integer) 
 * \return status code
 */
int MPI_Keyval_free(int *keyval)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Stores attribute value associated with a key 
 * \param comm communicator to which attribute will be attached (handle)
 * \param keyval key value, as returned by MPI_KEYVAL_CREATE (integer)
 * \param attribute_val attribute value 
 * \return status code
 */
int MPI_Attr_put(MPI_Comm comm, int keyval, void *attribute_val)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Retrieves attribute value by key 
 * \param comm communicator to which attribute will be attached (handle)
 * \param keyval key value, as returned by MPI_KEYVAL_CREATE (integer)
 * \param attribute_val attribute value 
 * \param flag true if an attribute value was extracted; false if no attribute is associated with the key  
 * \return status code
 */
int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute_val, int *flag)
{
	/* stub */
	return MPI_SUCCESS;
}

/**
 * \brief Deletes attribute value associated with a key
 * \param comm communicator to which attribute is attached (handle) 
 * \param keyval The key value of the deleted attribute (integer)
 * \return status code
 */
int MPI_Attr_delete(MPI_Comm comm, int keyval)
{
	/* stub */
	return MPI_SUCCESS;
}

int negotiate_context_tag(Comm *comm, Group *group,
	uid_pool *context_tag_pool)
{
	int my_candidate, this_candidate, new_context_tag, i, not_used;
	MPI_Request request_handle;
	Request *request;

	/* find our candidate context tag */
	
	uid_next(context_tag_pool, &my_candidate);
	new_context_tag = my_candidate;
	
	for (i = 0; i < group->num_members; i++)
	{
		if (i == comm->source)
		{
			/* broadcast our candidate tag to the rest of the group */
			
			this_candidate = my_candidate;
			CM_SendAll(&my_candidate, 1, MPI_INT, comm->source, comm,
				&request);
		}
		else
		{
			/* receive the next candidate tag from another group member */
			
			CM_Recv(&this_candidate, 1, MPI_INT, i, comm->source,
				comm->coll_context, MPI_ANY_TAG, &request_handle);
			request = OOM_request_lookup(request_handle);
			CM_Wait(request, &not_used, &not_used, &not_used);
		}
		
		/* if this candidate is larger than the current winner, it is the
		   new leading candidate tag */
		
		if (this_candidate > new_context_tag)
		{
			new_context_tag = this_candidate;
		}
	}
	
	return new_context_tag;
}
